#include "my_chash.h"
#include "my_mem.h"

#define MY_CHASH_GET_MAX_LOOP                   16

void
my_chash_add_node(my_chash_t *chash, my_str_t *key, void *data, int weight)
{
    my_chash_node_t *node = my_calloc(sizeof(my_chash_node_t));
    my_chash_node_init(node, key, data, weight);
    my_queue_insert_head(&chash->head, &node->queue);
    chash->nnode++;
}

void
my_chash_build(my_chash_t *chash)
{
    if (!my_rbtree_empty(&chash->tree)) {
        MY_LOG_FATAL("repeated build");
    }

    my_chash_node_t *node;
    my_chash_vnode_t *vnode;
    my_queue_t *q = my_queue_last(&chash->head);
    while(q != &chash->head) {
        node = my_queue_data(q, my_chash_node_t, queue);
        q = q->prev;

        if (node->weight <= 0) {
            continue;
        }

        for (int i = 0; i < chash->replace * node->weight; i++) {
            vnode = my_calloc(sizeof(my_chash_vnode_t));
            my_chash_vnode_init(vnode, node, i);
            my_rbtree_insert(&chash->tree, &vnode->tree_node);
        }
    }
}

void *
my_chash_get(my_chash_t *chash, my_str_t *key, my_chash_node_check_pt checker)
{
    if (chash->nnode <= 0) {
        return NULL;
    }

    my_queue_t *q;
    my_chash_node_t *node;
    my_chash_vnode_t *vnode;
    my_rbtree_node_t *tree_node;

    if (chash->nnode == 0) {
        return NULL;
    }

    if (chash->nnode == 1) {
        q = my_queue_last(&chash->head);
        node = my_queue_data(q, my_chash_node_t, queue);

        if (checker && !checker(node)) {
            return NULL;
        }

        return node->data;
    }

    uint32_t crc = my_crc32_short((const u_char *)key->data, (size_t)key->len);
    my_chash_vnode_t target = {.crc_key = crc};
    tree_node = my_rbtree_ge(&chash->tree, &target.tree_node);

    if (!tree_node) {
        tree_node = my_rbtree_min(chash->tree.root, chash->tree.sentinel);
    }

    int max_loop = MY_MAX(chash->nnode, MY_CHASH_GET_MAX_LOOP);
    for (int i = 0; i < max_loop; i++) {
        vnode = (my_chash_vnode_t *)tree_node;
        if (checker && !checker(vnode->real_node)) {
            tree_node = my_rbtree_next(&chash->tree, tree_node);
            continue;
        }

        return vnode->real_node->data;
    }

    return NULL;
}

void
my_chash_release(my_chash_t *chash)
{
    my_rbtree_node_t *tree_node;
    my_chash_vnode_t *vnode;

    while(!my_rbtree_empty(&chash->tree)) {
        tree_node = my_rbtree_min(chash->tree.root, chash->tree.sentinel);
        my_rbtree_delete(&chash->tree, tree_node);
        vnode = (my_chash_vnode_t *)tree_node;
        my_free(vnode);
    }

    my_queue_t      *q;
    my_chash_node_t *node;
    while(!my_queue_empty(&chash->head)) {
        q = my_queue_last(&chash->head);
        my_queue_remove(q);
        node = my_queue_data(q, my_chash_node_t, queue);
        my_free(node);
    }
}